/*
 * Copyright (C) 2022 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import { BaseElement, element } from '../../BaseElement';
import { lineChartColors } from './LitLineChartData';

@element('lit-line-chart')
export class LitLineChart extends BaseElement {
  private tipEL: HTMLDivElement | undefined | null;
  private maxValue: number = 10;
  private minValue: number = 0;
  private yMin: number = 0;
  private yMax: number = 100;
  private unitWidth: number = 0;
  private unitHeight: number = 0;
  private scale: number = 0;
  private canvas: HTMLCanvasElement | undefined | null;
  private context: CanvasRenderingContext2D | undefined | null;
  private canvasBottom: number = 0;
  private canvasTop: number = 0;
  private canvasLeft: number = 0;
  private points: Array<any> = [];
  private axisNumber: number = 6;
  private chartData: any | undefined;
  private filter: any;
  private interval: number = 1;
  private dataType: string = '';
  private lineColor: string = '#5b8ff9';
  private chartList: any = {};

  filterData(dataType: Array<string>, color: string) {
    this.lineColor = color;
    this.chartData = {};
    this.dataType = dataType[0];
    dataType?.forEach((type) => {
      this.chartData[type] = this.filter[type];
    });
    this.setLineChartData();
  }

  dataSource(value: any, chartList: any, isShowMore: boolean = false) {
    this.chartData = {};
    this.chartList = chartList;
    this.filter = value;
    let chartKeys = Object.keys(chartList);
    if (this.dataType === '') {
      this.dataType = chartKeys[0];
    }
    if (isShowMore) {
      this.chartData = value;
    } else {
      this.chartData[this.dataType] = this.filter[this.dataType];
    }
    this.setLineChartData();
  }

  connectedCallback() { }

  initElements(): void {
    this.initCanvas();
    this.addEventListener('mousemove', (e) => {
      if (this.chartData) {
        this.paint();
        this.displayTip(e);
      }
    });
    this.addEventListener('mouseout', (e) => {
      this.tipEL!.style.display = 'none';
      if (this.chartData) {
        this.paint();
      }
    });
    new ResizeObserver((entries) => {
      this.initCanvas();
      if (this.chartData) {
        this.setLineChartData();
      }
    }).observe(this);
  }

  setLineChartData() {
    this.minValue = 0;
    this.maxValue = 0;
    let data: unknown = [];
    for (const key in this.chartData) {
      // @ts-ignore
      data = data.concat(this.chartData[key]);
    }
    // @ts-ignore
    this.maxValue = Math.max.apply(Math, data.map((it) => it.value));
    // @ts-ignore
    this.minValue = Math.min.apply(Math, data.map((it) => it.value));
    // @ts-ignore
    if (data.length / Object.keys(this.chartData).length > 1) {
      // @ts-ignore
      this.unitWidth = (this.canvas!.width - this.canvasLeft * 2) / (data.length / Object.keys(this.chartData).length - 1);
    } else {
      this.unitWidth = this.canvas!.width - this.canvasLeft * 2;
    }
    this.unitHeight = (this.canvas!.height - this.canvasBottom - this.canvasTop) / this.axisNumber;

    if (this.maxValue === this.minValue) {
      this.maxValue = this.minValue + 10;
    }
    this.scale = (this.maxValue - this.minValue) / this.axisNumber;
    this.paint();
  }

  paint() {
    this.context!.clearRect(-this.canvasLeft, -this.canvasTop, this.canvas!.width, this.canvas!.height);
    this.drawParallelLine();
    this.drawXAxis();
    this.drawYAxis();
    this.drawBrokenLine();
    this.drawLegend();
  }

  private displayTip(e: MouseEvent) {
    let myRect = this.getBoundingClientRect();
    if (this.tipEL) {
      let html = '';
      let keys = Object.keys(this.chartData);
      keys.forEach((key, subscript) => {
        let index = Math.round(
          (e.offsetX - this.canvasLeft) /
          ((this.canvas!.width - this.canvasLeft * 2) / (this.chartData[key].length - 1))
        );
        if (subscript == 0 && index < this.chartData[key].length && index >= 0) {
          html += `<div>${this.chartData[key][index].date}</div>`;
        }
        if (index < this.chartData[key].length && index >= 0) {
          if (keys.length > 1) {
            html += `<div style="display: flex; margin-top: 15px;justify-content: flex-start;align-items: center"><div style="background-color: ${lineChartColors[subscript]}; width: 20px;height: 10px; margin-right: 15px"></div>${this.chartList[key]}: ${this.chartData[key][index].value}</div>`;
          } else {
            html += `<div style="display: flex; margin-top: 15px;justify-content: flex-start;align-items: center"><div style="background-color: ${this.lineColor}; width: 20px;height: 10px; margin-right: 15px"></div>${this.chartList[key]}:  ${this.chartData[key][index].value}</div>`;
          }
          this.context!.beginPath();
          this.context!.strokeStyle = '#CCCCCC';
          this.context!.lineWidth = 1;
          this.context!.moveTo(index * this.unitWidth, 0);
          this.context!.lineTo(index * this.unitWidth, this.canvas!.height - this.canvasTop - this.canvasBottom);
          this.context!.closePath();
          this.context!.stroke();
        }
      });
      this.tipEL.innerHTML = `<span>${html}</span>`;
      this.tipEL.style.display = 'flex';
      if (
        e.offsetX < this.canvas!.width - this.canvasLeft &&
        e.offsetX > this.canvasLeft &&
        e.offsetY < this.canvas!.height - this.canvasTop * 2 &&
        e.offsetY > this.canvasTop
      ) {
        if (e.clientX + this.tipEL.clientWidth > myRect.right) {
          this.tipEL.style.transform = `translateX(${e.offsetX - this.tipEL.clientWidth}px) translateY(${e.offsetY - this.tipEL.clientHeight / 2}px)`;
        } else {
          this.tipEL.style.transform = `translateX(${e.offsetX}px) translateY(${e.offsetY - this.tipEL.clientHeight / 2}px)`;
        }
      } else {
        this.tipEL.style.display = 'none';
      }
    }
  }

  private drawLegend() {
    let legends = this.shadowRoot!.querySelector<HTMLCanvasElement>('.legends');
    legends!.innerHTML = '';
    let keys = Object.keys(this.chartData);
    keys.forEach((key, index) => {
      if (keys.length > 1) {
        this.lineColor = lineChartColors[index];
      }
      let legend = document.createElement('div');
      legend.classList.add('legend');
      let legendBlock = document.createElement('div');
      legendBlock.className = 'block';
      legendBlock.style.backgroundColor = this.lineColor;
      legend.appendChild(legendBlock);
      let legendText = document.createElement('div');
      legendText.innerText = this.chartList[key];
      legend.appendChild(legendText);
      legends!.appendChild(legend);
    });
  }

  initCanvas() {
    this.tipEL = this.shadowRoot?.querySelector<HTMLDivElement>('.tip');
    this.canvasBottom = 50;
    this.canvasTop = 25;
    this.canvasLeft = 40;
    this.canvas = this.shadowRoot!.querySelector<HTMLCanvasElement>('#mycanvas');
    if (this.canvas != null) {
      this.canvas.width = this.canvas.clientWidth;
      this.canvas.height = this.canvas.clientHeight;
      this.context = this.canvas!.getContext('2d');
      this.context!.strokeStyle = '#DEDEDE';
      this.context!.scale(1, -1);
      this.context!.translate(this.canvasLeft, -this.canvas.height + this.canvasBottom);
    }
  }

  //绘制折线
  private drawBrokenLine() {
    let keys = Object.keys(this.chartData);
    keys.forEach((key, index) => {
      if (keys.length > 1) {
        this.lineColor = lineChartColors[index];
      }
      this.points = [];
      for (let i = 0; i < this.chartData[key].length; i++) {
        let Coordinate = {
          createNewPoint: function (pointX: any, pointY: any) {
            return {
              x: pointX,
              y: pointY,
            };
          },
        };
        let unitPixelHeight = (this.canvas!.height - this.canvasTop - this.canvasBottom) / (this.yMax - this.yMin);
        this.points.push(
          Coordinate.createNewPoint(this.unitWidth * i, (this.chartData[key][i].value - this.yMin) * unitPixelHeight)
        );
      }
      // 绘制折线
      this.context!.save();
      this.context!.beginPath();
      this.context!.lineWidth = 2;
      for (let j = 0; j < this.points.length; j++) {
        this.context!.lineTo(this.points[j].x, this.points[j].y);
      }
      // @ts-ignore
      this.context!.strokeStyle = this.lineColor;
      this.context!.stroke();
      this.context!.closePath();
      this.context!.restore();
      // 绘制折点
      this.context!.save();
      for (let j = 0; j < this.points.length; j++) {
        this.context!.beginPath();
        // @ts-ignore
        this.context!.strokeStyle = this.lineColor;
        this.context!.fillStyle = '#FFFFFF';
        this.context!.lineWidth = 2;
        this.context!.arc(this.points[j].x, this.points[j].y, 3, 0, Math.PI * 2, true);
        this.context!.closePath();
        this.context!.stroke();
        this.context!.shadowBlur = 5;
        this.context!.fill();
      }
      this.context!.restore();
    });
  }

  private drawParallelLine() {
    this.context!.save();
    this.context!.strokeStyle = '#DEDEDE';
    this.context!.lineWidth = 2;
    for (let i = 0; i <= this.axisNumber; i++) {
      this.context!.beginPath();
      if (i != 0) {
        this.context!.setLineDash([6]);
        this.context!.strokeStyle = '#F6F7F8';
      }
      this.context!.moveTo(0, this.unitHeight * i);
      this.context!.lineTo(this.canvas!.width - this.canvasLeft * 2, this.unitHeight * i);
      this.context!.closePath();
      this.context!.stroke();
    }
    this.context!.restore();
    this.context!.save();
  }

  private drawXAxis() {
    this.context!.strokeStyle = '#DEDEDE';
    this.context!.lineWidth = 2;
    this.interval = 1;
    let maxValueWidth = this.context!.measureText(
      this.chartData[this.dataType][this.chartData[this.dataType].length - 1].date
    );
    let xValueWidth = Math.ceil((this.canvas!.width - this.canvasLeft * 2) / this.chartData[this.dataType].length);
    if (maxValueWidth.width + 10 > xValueWidth) {
      this.interval = 3;
    }
    for (let i = 0; i < this.chartData[this.dataType].length / this.interval; i++) {
      this.context!.beginPath();
      this.context!.moveTo(0, 0);
      this.context!.lineTo(0, -5);
      this.context!.closePath();
      this.context!.stroke();
      this.context!.translate(this.unitWidth * this.interval, 0);
    }
    this.context!.restore();
    this.context!.save();
    this.context!.scale(1, -1);
    this.context!.font = '12px Arial';
    this.context!.fillStyle = '#000000';
    for (let i = 0; i < this.chartData[this.dataType].length / this.interval; i++) {
      this.context!.stroke();
      if (i === 0) {
        this.context!.translate(0, 15);
      } else {
        this.context!.translate(this.unitWidth * this.interval, 0);
      }
      let textWidth = this.context!.measureText(this.chartData[this.dataType][i * this.interval].date);
      this.context!.fillText(this.chartData[this.dataType][i * this.interval].date, -textWidth.width / 2, 0);
    }
    this.context!.restore();
  }

  private drawYAxis() {
    this.yMin = Math.floor(this.minValue - this.scale / 2 < 0 ? 0 : this.minValue - this.scale / 2);
    this.yMax = Math.ceil(this.maxValue + this.scale / 2);
    let axisScale = Math.ceil((this.yMax - this.yMin) / this.axisNumber);
    this.yMax = this.yMin + axisScale * this.axisNumber;
    this.context!.save();
    this.context!.scale(1, -1);
    let axisWidth = this.context!.measureText(this.yMax.toString()).width;
    this.context!.translate(-axisWidth, 0);
    this.context!.font = '12px Arial';
    this.context!.fillStyle = '#000000';
    for (let i = 0; i <= this.axisNumber; i++) {
      this.context!.stroke();
      this.context!.fillText((this.yMin + axisScale * i).toString(), -5, 0);
      this.context!.translate(0, -this.unitHeight);
    }
    this.context!.restore();
  }

  initHtml(): string {
    return `
        <style>
        :host {
            display: block;
            width: auto;
            height: 100%;
            background: background: var(--dark-background3,#FFFFFF);
        }
        .block {
          width:30px;
          height: 5px;
          margin-right: 10px;
        }
        .legends {
            display: flex;
            justify-content:center;
        }
        .legend {
            display: flex;
            align-items: center;
            justify-content: center;
            margin-right: 40px;
        }
        .tip {
           display: none;
           width: fit-content;
           background-color: #FFFFFF;
           box-shadow: rgb(174 174 174) 0px 0px 10px;
           padding: 10px;
           opacity: 0.8;
        }
        </style>
        <div style="height: 100%">
            <div id="tip" style="position: absolute;" class="tip"></div>
            <canvas id="mycanvas" style="height: 95%;width: 100%"></canvas>
            <div class="legends"></div>
        </div>
      `;
  }
}
